package argparse

import (
	"fmt"
	"reflect"
	"strings"
)

type NArgs byte

const (
	NArgsNone       NArgs = '0'
	NArgsOne        NArgs = '1'
	NArgsAtMostOne  NArgs = '?'
	NArgsAtLeastOne NArgs = '+'
	NArgsAny        NArgs = '*'
)

func (na NArgs) String() string {
	return fmt.Sprintf("nargs(%c)", na)
}

type Option struct {
	Name     string
	Short    byte
	Long     string
	Type     reflect.Type
	Required bool
	Default  string
	NArgs    NArgs
	Help     string
}

func parseOption(name, tag string, typ reflect.Type) (opt *Option, err error) {
	opt = &Option{
		Name:     name,
		Short:    0,
		Long:     "",
		Required: false,
		Type:     typ,
		Default:  "",
		NArgs:    NArgsOne,
		Help:     "",
	}
	if tag == "" {
		return opt, nil
	}

	for _, optarg := range strings.Split(tag, ",") { // TODO help support ","
		kv := strings.SplitN(optarg, "=", 2)
		if len(kv) == 1 {
			if err = opt.addKey(kv[0]); err != nil {
				return nil, err
			}
		} else if err = opt.addKeyValue(kv[0], kv[1]); err != nil {
			return nil, err
		}
	}

	if opt.Type.Kind() == reflect.Bool {
		if opt.Default == "" {
			opt.Default = "false"
		}
		opt.NArgs = NArgsNone
	}

	return opt, nil
}

func (opt *Option) isPositional() bool { // TODO support nargs
	return opt.Short == 0 && opt.Long == ""
}

func (opt *Option) addKey(k string) error {
	switch k {
	case "required":
		opt.Required = true
		opt.NArgs = NArgsOne
	default:
		return fmt.Errorf("unknown argument: `%s'", k)
	}
	return nil
}

func (opt *Option) addKeyValue(k, v string) error {
	switch k {
	case "short":
		if len(v) != 1 {
			return fmt.Errorf("invalid short flag: %s", v)
		}
		opt.Short = v[0]
	case "long":
		opt.Long = v // TODO validate long flag
	case "default":
		opt.Default = v
	case "nargs":
		if len(v) != 1 {
			return fmt.Errorf("invalid nargs flag: %s", v)
		}
		switch flg := v[0]; flg {
		case '?':
			opt.NArgs = NArgsAtMostOne
		case '+':
			opt.NArgs = NArgsAtLeastOne
		case '*':
			opt.NArgs = NArgsAny
		default:
			return fmt.Errorf("unsupported nargs flag: %s", v)
		}
	case "help":
		opt.Help = v
	default:
		return fmt.Errorf("unknown key: %s", k)
	}
	return nil
}
